#!/bin/sh

# list online cpus
# list all cpus by default (with no arg)
# $1 - socket: only list cpus of given socket
# $2 - core:   only list cpus of given core
list_online_cpu()
{
	local socket=$1
	local core=$2
	local online_cpus
	local cpu
	local index_start
	local index_end
	local socket_id
	local core_id

	# get online cpu list: should work on host, vm, and container
	# the online cpu list may look like: 0-3,7,9-11
	for cpu in `cat /sys/devices/system/cpu/online | sed 's/,/\ /g'`
	do
		if echo $cpu | egrep -q "[0-9]*-[0-9]*"; then
			index_start=`echo $cpu | cut -d- -f1`
			index_end=`echo $cpu | cut -d- -f2`
			for c in `seq $index_start $index_end`; do
				online_cpus+=" $c"
			done
		else
			online_cpus+=" $cpu"
		fi
	done

	for cpu in $online_cpus; do
		socket_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/physical_package_id`
		if [ -n "$socket" ] && \
			[ "$socket" != "$socket_id" ]; then
			continue
		fi
		core_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/core_id`
		if [ -n "$core" ] && \
			[ "$core" != "$core_id" ]; then
			continue
		fi
		echo $cpu
	done
}

# list online cpus with topology
# list all cpus by default (with no arg)
# $1 - socket: only list cpus of given socket
# $2 - core:   only list cpus of given core
list_online_cpu_tpy()
{
	local socket=$1
	local core=$2
	local cpu
	local socket_id
	local core_id

	for cpu in `list_online_cpu`; do
		socket_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/physical_package_id`
		if [ -n "$socket" ] && \
			[ "$socket" != "$socket_id" ]; then
			continue
		fi
		core_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/core_id`
		if [ -n "$core" ] && \
			[ "$core" != "$core_id" ]; then
			continue
		fi
		echo "${socket_id}-${core_id}-${cpu}"
		# sort the online cpus by topology: socket-core_id-cpu_id
	done | sort -V
}

# list available cpu cores
# list all cpus by default (with no arg)
# $1 - socket: only list cores of given socket
list_cpu_core()
{
	local socket=$1
	local cpu
	local socket_id
	local core_id

	for cpu in `list_online_cpu`; do
		socket_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/physical_package_id`
		if [ -n "$socket" ] && \
			[ "$socket" != "$socket_id" ]; then
			continue
		fi
		core_id=`cat /sys/devices/system/cpu/cpu${cpu}/topology/core_id`
		echo "$core_id"
	done | sort -u
}

# list available cpu sockets
list_cpu_socket()
{
	local cpu
	for cpu in `list_online_cpu`; do
		cat /sys/devices/system/cpu/cpu$cpu/topology/physical_package_id
	done | sort -u
}

# function to get online cpu list
# export following env variables:
# cpu_online_num: number of online cpu
# cpu_online_lst: list of online cpu sorted by index
# cpu_online_tpy: list of online cpu with topology info
check_oneline_cpu()
{
	# global variables
	cpu_online_num=0
	cpu_online_lst=""
	cpu_online_tpy=""

	# local variables
	local cpu

	# get online cpu list
	for cpu in `list_online_cpu`; do
		cpu_online_lst+=" $cpu"
		((cpu_online_num++))
	done
	# get the topology of online cpus
	for cpu in `list_online_cpu_tpy`; do
		cpu_online_tpy+=" $cpu"
	done

	export cpu_online_num
	export cpu_online_lst
	export cpu_online_tpy
}

# function to set cpu affinity for server program
# - add "taskset -c $cpu" prefix to server_cmd
set_cpu_affinity_server()
{
	local socket
	local core
	local cpu

	[ -n "$cpu_affinity" ] || return
	[ `nproc` -gt 1 ] || return

	# bind to last socket if multi sockets exist
	socket=`list_cpu_socket | tail -n 1`
	# bind to last core if multi cores on socket
	core=`list_cpu_core "$socket" | tail -n 1`
	# bind to last thread if multi threads on core
	cpu=`list_online_cpu "$socket" "$core" | tail -n 1`
	server_cmd="taskset -c $cpu $server_cmd"
	echo "run server on cpu: $cpu, socket: $socket, core: $core"
}

# function to set cpu affinity for client program
# - add "taskset -c $cpu" prefix to client_cmd
set_cpu_affinity_client()
{
	local socket
	local core
	local cpu

	[ -n "$cpu_affinity" ] || return
	[ `nproc` -gt 1 ] || return

	# bind to first socket if multi sockets exist
	socket=`list_cpu_socket | head -n 1`
	# bind to first core if multi cores on socket
	core=`list_cpu_core "$socket" | head -n 1`
	# bind to first thread if multi threads on core
	cpu=`list_online_cpu "$socket" "$core" | head -n 1`
	client_cmd="taskset -c $cpu $client_cmd"
	echo "run client on cpu: $cpu, socket: $socket, core: $core"
}
